#include "gxhtc3.h"         // 包含 GXHT3 温湿度传感器驱动头文件
#include "myi2c.h"          // 包含自定义的I2C初始化函数
#include "driver/i2c.h"     // 包含I2C驱动程序的头文件
#include <math.h>           //  包含数学函数库头文件

uint8_t humi_data[6];       // 用于存储温湿度传感器数据的数组

uint16_t rawValueTemp, rawValueHumi;// 原始温度值，原始湿度值
float 温度 = 0, 湿度 = 0;
uint8_t 整数温度, 整数湿度;

#define 多项式  0x31 // P(x) = x^8 + x^5 + x^4 + 1 = 00110001， CRC校验多项式

// CRC校验
uint8_t gxhtc3_冗余校验(uint8_t *CRC数据, uint8_t 长度) // CRC校验函数
{
    uint8_t 循环冗余 = 0xFF;                            //  CRC校验的初始值

    for (uint8_t i = 0; i < 长度; i++)                  // 循环遍历数据
    {
        循环冗余 ^= (CRC数据[i]);                       // 与数据进行异或运算
        for (uint8_t j = 8; j > 0; --j)                 // 循环进行CRC校验
        {
            if (循环冗余 & 0x80) 循环冗余 = (循环冗余 << 1) ^ 多项式; // 如果最高位为1，进行CRC校验
            else           循环冗余 = (循环冗余 << 1);                // 如果最高位为0，不进行CRC校验
        }                   
    }
    return 循环冗余;                                                   // 返回CRC校验结果
}


// gxhtc3连接i2c总线
esp_err_t gxhtc3_I2C(uint8_t a, uint8_t b)
{
    esp_err_t 返回值;
    i2c_cmd_handle_t 命令 = i2c_cmd_link_create();      // i2c_cmd_link_create()函数创建一个I2C命令句柄,用于发送I2C命令 
    i2c_master_start(命令);                             // i2c_master_start()函数发送I2C起始信号

    if (a > 0 && b > 0)                                  // 如果a和b都大于0，则发送写命令
    {
        i2c_master_write_byte(命令, 0x70 << 1 | I2C_MASTER_WRITE, true);    // i2c_master_write_byte()函数发送I2C写命令,I2C_MASTER_WRITE表示写操作
        i2c_master_write_byte(命令, a, true);               
        i2c_master_write_byte(命令, b, true);
    }
    else
    {
        i2c_master_write_byte(命令, 0x70 << 1 | I2C_MASTER_READ, true);     // i2c_master_write_byte()函数发送I2C读命令，I2C_MASTER_READ表示读操作
        i2c_master_read(命令, humi_data, 6, I2C_MASTER_LAST_NACK);          // i2c_master_read()函数发送I2C读命令
    }

    i2c_master_stop(命令);                              // i2c_master_stop()函数发送I2C停止信号
    返回值 = i2c_master_cmd_begin(I2C主端口, 命令, 1000 / portTICK_PERIOD_MS);// i2c_master_cmd_begin()函数发送I2C命令
    i2c_cmd_link_delete(命令);                          // i2c_cmd_link_delete()函数删除I2C命令句柄
    return 返回值;                                      // 返回值为ESP_OK表示成功，ESP_FAIL表示失败，其他值表示内容，具体值参考esp_err.h文件
}

// 读取ID
esp_err_t gxhtc3_读取id(void)                             
{
    esp_err_t 返回值;

    // 发送读取ID的命令
    返回值 = gxhtc3_I2C(0xEF, 0xC8);                    // 发送读取ID的命令，0xEF表示寄存器地址，0xC8表示命令码
    if (返回值 != ESP_OK) {                             //  如果返回值不为ESP_OK，则返回错误
        return 返回值;
    }

    // 读取ID数据
    返回值 = gxhtc3_I2C(0, 0);                          //  发送读取ID数据的命令，0表示读取数据，0表示读取数据长度为6
    if (返回值 != ESP_OK) {                             //  如果返回值不为ESP_OK，则返回错误
        return 返回值;
    }

    // 验证CRC校验
    if (humi_data[2] != gxhtc3_冗余校验(humi_data, 2)) {    //  如果CRC校验失败，则返回错误
        return ESP_FAIL;                                    //  返回错误
    }

    return 返回值;                                          //  返回值为ESP_OK表示成功，ESP_FAIL表示失败，其他值表示内容，具体值参考esp_err.h文件
}

// 获取并计算温湿度数据
esp_err_t gxhtc3_取温湿度(void)
{
    esp_err_t 返回值;

    gxhtc3_I2C(0x35, 0x17);                 // 唤醒
    gxhtc3_I2C(0x7c, 0xa2);                 // 测量
    vTaskDelay(50 / portTICK_PERIOD_MS);    // 增加延时时间
    gxhtc3_I2C(0, 0);                       // 读取温湿度数据
    gxhtc3_I2C(0xB0, 0x98);                 // 休眠

    if ((humi_data[2] != gxhtc3_冗余校验(humi_data, 2)) || (humi_data[5] != gxhtc3_冗余校验(&humi_data[3], 2)))
    //                 温度CRC校验       湿度CRC校验
    {
        温度 = 0;
        湿度 = 0;
        整数温度 = 0;
        整数湿度 = 0;
        返回值 = ESP_FAIL;
    }
    else
    {
        rawValueTemp = (humi_data[0] << 8) | humi_data[1];      // 计算原始温度值
        rawValueHumi = (humi_data[3] << 8) | humi_data[4];      // 计算原始湿度值
        温度 = (175.0 * (float)rawValueTemp) / 65535.0 - 45.0;  // 计算温度值
        湿度 = (100.0 * (float)rawValueHumi) / 65535.0;         // 计算湿度值
        整数温度 = round(温度);                                 //  取整温度值
        整数湿度 = round(湿度);                                 //  取整湿度值
        返回值 = ESP_OK;
    }
    return 返回值;
}